package com.security.defend.wall;

import com.security.defend.wall.ConstData;
import com.security.defend.wall.ShellEx;

import android.os.Build;

import android.content.Context;

import androidx.annotation.NonNull;

import com.scottyab.rootbeer.RootBeer;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;


public class RootedChecker {

    /**
     * 验证环境是否被Root
     *
     * @param context 上下文
     */
    public boolean isRooted(Context context) {
        boolean detected = false;
        RootBeer rootBeer = new RootBeer(context);
        if (Build.BRAND.contains("oneplus") || Build.BRAND.contains("moto") || Build.BRAND.contains("Xiaomi")) {
            if (rootBeer.isRooted()) {
                detected = true;
                return detected;
            }
        } else {
            if (rootBeer.isRootedWithBusyBoxCheck()) {
                detected = true;
                return detected;
            }
        }

        try {
            if (isPathExist("su")
                    || isSUExist()
                    || isTestBuildKey()
                    || isHaveRootHideApps(context)
                    || isHaveDangerousApps(context)
                    || isHaveRootManagementApps(context)
                    || isHaveDangerousProperties()
                    || isHaveReadWritePermission()) {
                detected = true;
                return detected;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            if (checkRunSuRoot() || checkSuRoot()) {
                detected = true;
                return detected;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return detected;
    }


    private boolean isPathExist(String ext) {
        for (String path : ConstData.superUserPath) {
            File file = new File(path, ext);
            if (file.exists()) {
                return true;
            }
        }
        return false;
    }

    private boolean isSUExist() {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[]{"/system/xbin/which", "su"});
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            if (in.readLine() != null) {
                return true;
            }
            return false;
        } catch (Exception e) {
            return false;
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
    }


    private boolean isTestBuildKey() {
        String buildTags = Build.TAGS;
        return buildTags != null && buildTags.contains("test-keys");
    }

    private boolean isHaveDangerousApps(Context context) {
        ArrayList<String> packages = new ArrayList<String>();
        packages.addAll(Arrays.asList(ConstData.dangerousListApps));
        return isAnyPackageFromListInstalled(context, packages);
    }

    private boolean isHaveRootManagementApps(Context context) {
        ArrayList<String> packages = new ArrayList<>();
        packages.addAll(Arrays.asList(ConstData.rootsAppPackage));
        return isAnyPackageFromListInstalled(context, packages);
    }

    private boolean isHaveRootHideApps(Context context) {
        ArrayList<String> packages = new ArrayList<>();
        packages.addAll(Arrays.asList(ConstData.rootCloakingApps));
        return isAnyPackageFromListInstalled(context, packages);
    }


    //check dangerous properties
    private boolean isHaveDangerousProperties() {
        final Map<String, String> dangerousProps = new HashMap<>();
        dangerousProps.put("ro.debuggable", "1");
        dangerousProps.put("ro.secure", "0");

        boolean result = false;
        String[] lines = commander("getprop");
        if (lines == null) {
            return false;
        }
        for (String line : lines) {
            for (String key : dangerousProps.keySet()) {
                if (line.contains(key)) {
                    String badValue = dangerousProps.get(key);
                    badValue = "[" + badValue + "]";
                    if (line.contains(badValue)) {
                        result = true;
                    }
                }
            }
        }
        return result;
    }


    private boolean isHaveReadWritePermission() {
        boolean result = false;
        String[] lines = commander("mount");

        for (String line : lines) {
            String[] args = line.split(" ");
            if (args.length < 4) {
                continue;
            }
            String mountPoint = args[1];
            String mountOptions = args[3];

            for (String path : ConstData.notWritablePath) {
                if (mountPoint.equalsIgnoreCase(path)) {
                    for (String opt : mountOptions.split(",")) {
                        if (opt.equalsIgnoreCase("rw")) {
                            result = true;
                            break;
                        }
                    }
                }
            }
        }
        return result;
    }


    public boolean checkRunSuRoot() {
        boolean result = new ShellEx().executeCommandSU(ShellEx.SHELL_CMD.run_su);
        return result;
    }

    public boolean checkSuRoot() {
        ArrayList<String> result = new ShellEx().executeCommand(ShellEx.SHELL_CMD.check_su);
        if (result != null) {
            for (String tempString : result) {
                if (tempString.endsWith("su"))
                    return true;
            }
        }
        return false;
    }

    private boolean isAnyPackageFromListInstalled(Context context, ArrayList<String> pkg) {
        boolean result = false;
        PackageManager pm = context.getPackageManager();
        for (String packageName : pkg) {
            try {
                pm.getPackageInfo(packageName, 0);
                result = true;
            } catch (Exception e) {

            }
        }
        return result;
    }

    private String[] commander(String command) {
        try {
            InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream();
            if (inputStream == null) {
                return null;
            }
            String propVal = new Scanner(inputStream).useDelimiter("\\A").next();
            return propVal.split("\n");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}